/**
 * 
 */
package gpl.scotlandyard.beans.basics;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.apache.log4j.Logger;

/** @author Norbert */
public final class Board {

  private static final Logger LOG = Logger.getLogger(Board.class);

  private final ConcurrentLinkedQueue<Listener> listeners = new ConcurrentLinkedQueue<Listener>();

  /** DETECTIVES_WIN */
  public static final int DETECTIVES_WIN = 1;

  /** FUGITIVE_HOLD */
  public static final int FUGITIVE_HOLD = 2;

  /** FUGITIVE_WIN */
  public static final int FUGITIVE_WIN = 3;

  public static final int MAX_TURN = 24;

  /** BoardListener is informed about board events.
   * @author Norbert Martin */
  public interface Listener {
    /** Called when the current player change for the next one.
     * @param player who takes its turn */
    void currentPlayerChanged(Player player);

    /** Called when the game finished
     * @param code associated */
    void gameFinished(int code);
  }

  /** BoardListener is informed about board events.
   * @author Norbert Martin */
  public static class ListenerImpl implements Listener {

    @Override
    public void currentPlayerChanged(Player player) {
    }

    @Override
    public void gameFinished(int code) {
    }

  }

  private int currentTurn = 0;

  private final List<Player> detectives = new ArrayList<Player>();

  private Player fugitive;
  private boolean isGameFinished;
  private List<Node> map;
  private int maxTurn;
  private final LinkedList<Player> playersQueue = new LinkedList<Player>();

  /** @param detective */
  public void addDetective(Player detective) {
    detectives.add(detective);
    playersQueue.addLast(detective);
  }

  /** @return the currentPlayer */
  public Player getCurrentPlayer() {
    return playersQueue.getFirst();
  }

  /** @return the currentTurn */
  public int getCurrentTurn() {
    return currentTurn;
  }

  /** @return the detectives */
  public List<Player> getDetectives() {
    return detectives;
  }

  /** @return the fugitive */
  public Player getFugitive() {
    return fugitive;
  }

  /** @return the map */
  public List<Node> getMap() {
    return map;
  }

  /** @return the maxTurn */
  public int getMaxTurn() {
    return maxTurn;
  }

  /** @return the playersQueue */
  public LinkedList<Player> getPlayersQueue() {
    return playersQueue;
  }

  /** @return the isGameFinished */
  public boolean isGameFinished() {
    return isGameFinished;
  }

  /** @param isSamePlayer */
  public void nextPlayer(boolean isSamePlayer) {
    if (!isSamePlayer) {
      playersQueue.addLast(playersQueue.removeFirst());
      if (getCurrentPlayer().isFugitive()) {
        currentTurn++;
      }
    }
    fireNextPlayerTurn(getCurrentPlayer());
  }

  /** @param currentTurn the currentTurn to set */
  public void setCurrentTurn(int currentTurn) {
    this.currentTurn = currentTurn;
  }

  /** @param fugitive the fugitive to set */
  public void setFugitive(Player fugitive) {
    this.fugitive = fugitive;
    playersQueue.addFirst(fugitive);
  }

  /** @param isGameFinished the isGameFinished to set */
  public void setGameFinished(int code) {
    this.isGameFinished = true;
    fireGameFinished(code);
  }

  /** @param map the map to set */
  public void setMap(List<Node> map) {
    this.map = map;
  }

  /** @param maxTurn the maxTurn to set */
  public void setMaxTurn(int maxTurn) {
    this.maxTurn = maxTurn;
  }

  /**
   * 
   */
  public void start() {
    fireNextPlayerTurn(getCurrentPlayer());
  }

  /**
   * Adds listener.
   * @param listener to add
   */
  public void addListener(Board.Listener listener) {
    listener.getClass();
    listeners.add(listener);
  }

  /**
   * Removes listener if listening this model.
   * @param listener to remove
   */
  public void removeListener(Board.Listener listener) {
    listener.getClass();
    listeners.remove(listener);
  }

  /**
   * Fires nextPlayerTurn.
   * @param message
   */
  protected void fireNextPlayerTurn(Player player) {
    LOG.debug("fireNextPlayerTurn : " + player);
    for (Board.Listener l : listeners) {
      l.currentPlayerChanged(player);
    }
  }

  /**
   * Fires gameFinished.
   * @param code
   */
  protected void fireGameFinished(int code) {
    LOG.debug("fireGameFinished : " + code);
    for (Board.Listener l : listeners) {
      l.gameFinished(code);
    }
  }

}
